--
-- turb
--

require "BuildingObjects/ISBuildingObject"
local moveableModes = {"pickup","place","rotate","scrap"};
local function addMode(_tagTable, _tag, _titleTable, _title)
    table.insert(_tagTable, _tag);
    table.insert(_titleTable, _title);
end

ISMoveableCursor = ISBuildingObject:derive("ISMoveableCursor");
ISMoveableCursor.modes = {};
ISMoveableCursor.modes.tags = {};
ISMoveableCursor.modes.titles = {}
addMode(ISMoveableCursor.modes.tags,"pickup", ISMoveableCursor.modes.titles, getText("IGUI_Pickup"));
addMode(ISMoveableCursor.modes.tags,"place", ISMoveableCursor.modes.titles, getText("IGUI_Place"));
addMode(ISMoveableCursor.modes.tags,"rotate", ISMoveableCursor.modes.titles, getText("IGUI_Rotate"));
addMode(ISMoveableCursor.modes.tags,"scrap", ISMoveableCursor.modes.titles, getText("IGUI_Scrap"));
ISMoveableCursor.cursors = {};
ISMoveableCursor.mode = {}; --nil; --"pickup";
ISMoveableCursor.cacheMode = {}; --nil;
--ISMoveableCursor.cacheInvObject = nil;
ISMoveableCursor.normalColor = { r=0.5, g=0.5, b=0.5 };
ISMoveableCursor.validColor = { r=0.5, g=1, b=0.5 };
ISMoveableCursor.invalidColor = { r=1, g=0, b=0 };

--[[
function ISMoveableCursor.exitCursorKey( _key )
    if _key and type(_key)=="number" then
        ISMoveableCursor.exitCursor( _key );
    end
end
--]]

function ISMoveableCursor.exitCursorKey( _key )
    if getCell() and getCell():getDrag(0) and getCell():getDrag(0).Type and getCell():getDrag(0).Type == "ISMoveableCursor" then
        local disable = false;
        if not _key then
            disable = true;
        else
            if getCore():getKey("Run") == _key then
                disable = true;
            elseif getCore():getKey("Interact") == _key then
                disable = true;
            end
        end

        if disable then
            getCell():setDrag(nil, 0);
        end
    end
end

function ISMoveableCursor:exitCursor()
    getCell():setDrag(nil, self.player);
    if ISMoveableInfoWindow.infoPanels[self.player] then
        ISMoveableInfoWindow.infoPanels[self.player]:removeFromUIManager();
    end
end

Events.OnKeyPressed.Add(ISMoveableCursor.exitCursorKey);
Events.OnKeyKeepPressed.Add(ISMoveableCursor.exitCursorKey);
Events.OnRightMouseDown.Add(ISMoveableCursor.exitCursorKey);

function ISMoveableCursor:onObjectLeftMouseButtonDown(object, x, y)
    -- Disable clicks on objects, to prevent the loot window being displayed
    return true
end

function ISMoveableCursor.changeModeKey( _key, _playerNum, _joyPadTriggered )
    if _joyPadTriggered then
        local playerID = _playerNum or 0;
        if getCell() and getCell():getDrag(playerID) and getCell():getDrag(playerID).Type and
                getCell():getDrag(playerID).Type == "ISMoveableCursor" then
            local playerObj = getSpecificPlayer(playerID)
            local menu = getPlayerRadialMenu(playerID)
            menu:clear()
            menu:addSlice(getText("IGUI_Pickup"), getTexture("media/ui/Furniture_Pickup.png"), ISMoveableCursor.setMoveableMode, getCell():getDrag(playerID), "pickup")
            menu:addSlice(getText("IGUI_Place"), getTexture("media/ui/Furniture_Place.png"), ISMoveableCursor.setMoveableMode, getCell():getDrag(playerID), "place")
            menu:addSlice(getText("IGUI_Rotate"), getTexture("media/ui/Furniture_Rotate.png"), ISMoveableCursor.setMoveableMode, getCell():getDrag(playerID), "rotate")
            menu:addSlice(getText("IGUI_Scrap"), getTexture("media/ui/Furniture_Disassemble.png"), ISMoveableCursor.setMoveableMode, getCell():getDrag(playerID), "scrap")
            menu:setX(getPlayerScreenLeft(playerID) + getPlayerScreenWidth(playerID) / 2 - menu:getWidth() / 2)
            menu:setY(getPlayerScreenTop(playerID) + getPlayerScreenHeight(playerID) / 2 - menu:getHeight() / 2)
            menu:addToUIManager()
            menu:setHideWhenButtonReleased(Joypad.LBumper)
            setJoypadFocus(playerID, menu)
            playerObj:setJoypadIgnoreAimUntilCentered(true)
        end
        return
    end
    if _key == getCore():getKey("Toggle mode") or _joyPadTriggered then
        local playerID = _playerNum or 0;
        for k,v in ipairs(ISMoveableCursor.modes.tags) do
            if v == ISMoveableCursor.mode[playerID] then
                local next = k+1;
                if next > #ISMoveableCursor.modes.tags then
                    next = 1;
                end
                if getCell() and getCell():getDrag(playerID) and getCell():getDrag(playerID).Type and getCell():getDrag(playerID).Type == "ISMoveableCursor" then
                    getCell():getDrag(playerID):setMoveableMode( ISMoveableCursor.modes.tags[next] );
                end
                return;
            end
        end
    end
end

Events.OnKeyPressed.Add(ISMoveableCursor.changeModeKey);

function ISMoveableCursor.clearCacheForAllPlayers()
    for i = 0,3 do
        --print("Attempt to clear for player: "..tostring(i));
        if getCell() and getCell():getDrag(i) and getCell():getDrag(i).Type and getCell():getDrag(i).Type == "ISMoveableCursor" then
            getCell():getDrag(i):clearCache();
        end
    end
end

function ISMoveableCursor:clearCache()
    --if getCell() and getCell():getDrag(0) and getCell():getDrag(0).Type and getCell():getDrag(0).Type == "ISMoveableCursor" then
        --local drag = getCell():getDrag(0);
        if ISMoveableCursor.mode[self.player] == "rotate" then
            self.objectIndex = -1;
        end
        self.objectListCache = nil;
        self.cacheObject = nil;
    --end
end

function ISMoveableCursor:getMoveableMode()
    return ISMoveableCursor.mode[self.player];
end

function ISMoveableCursor:tryInitialItem( _item )
    self.tryInitialInvItem = _item;
end

function ISMoveableCursor:setMoveableMode( _mode )
    if _mode then
        for k,v in ipairs(ISMoveableCursor.modes.tags) do
            if v==_mode then
                ISMoveableCursor.mode[self.player] = _mode;
                --self.moveableMode = _mode;

                if not ISMoveableCursor.cacheMode[self.player] or ISMoveableCursor.cacheMode[self.player] ~= ISMoveableCursor.mode[self.player] then
                    local infoPanel = self:getInfoPanel();
                    local lastbit = ""; --""[br/]'"..Keyboard.getKeyName(getCore():getKey("Toggle Moveable Panel Mode")).."' = change panel display mode."; -- "[br/]'"..Keyboard.getKeyName(getCore():getKey("Toggle mode")).."' = toggle moveable modes.[br/]'"..Keyboard.getKeyName(getCore():getKey("Toggle Moveable Panel Mode")).."' = change panel display mode.";
                    if ISMoveableCursor.mode[self.player] == "pickup" then
                        infoPanel:setHeaderText( getText("IGUI_Pickup"), UIFont.Medium );
                        infoPanel:setFooterText( "'"..Keyboard.getKeyName(getCore():getKey("Rotate building")).."' = "..getText("IGUI_Cycle1").."."..lastbit, UIFont.Small );
                    elseif ISMoveableCursor.mode[self.player] == "place" then
                        infoPanel:setHeaderText( getText("IGUI_Place"), UIFont.Medium );
                        infoPanel:setFooterText( getText("IGUI_HoldButton")..".[br/]'"..Keyboard.getKeyName(getCore():getKey("Rotate building")).."' = "..getText("IGUI_Cycle2").."."..lastbit, UIFont.Small );
                    elseif ISMoveableCursor.mode[self.player] == "rotate" then
                        infoPanel:setHeaderText( getText("IGUI_Rotate"), UIFont.Medium );
                        infoPanel:setFooterText( getText("IGUI_HoldButton")..".[br/]'"..Keyboard.getKeyName(getCore():getKey("Rotate building")).."' = "..getText("IGUI_Cycle3").."."..lastbit, UIFont.Small );
                    elseif ISMoveableCursor.mode[self.player] == "scrap" then
                        infoPanel:setHeaderText( getText("IGUI_Scrap"), UIFont.Medium );
                        infoPanel:setFooterText( "'"..Keyboard.getKeyName(getCore():getKey("Rotate building")).."' = "..getText("IGUI_Cycle1").."."..lastbit, UIFont.Small );
                    end

                    ISMoveableCursor.cacheMode[self.player] = ISMoveableCursor.mode[self.player];
                    self.objectIndex = -1;
                    self.objectListCache = nil;
                    self.cacheObject = nil;
                end
            end
        end
    end
end

function ISMoveableCursor:create(_x, _y, _z, _north, _sprite)
    if self.canCreate and self.currentMoveProps and self.origSpriteName then
        if self.currentMoveProps then
            if ISMoveableDefinitions.cheat or self.currentMoveProps:walkToAndEquip( self.character, self.currentSquare, ISMoveableCursor.mode[self.player] ) then
                if ISMoveableCursor.mode[self.player] == "scrap" then
                    if self.currentMoveProps.object:getProperties():Is(IsoFlagType.solidfloor) then
                        local adjacent = AdjacentFreeTileFinder.Find(self.currentSquare, self.character)
                        if adjacent ~= nil then
                            ISTimedActionQueue.add(ISWalkToTimedAction:new(self.character, adjacent))
                        end
                    end
                end
                ISTimedActionQueue.add(ISMoveablesAction:new(self.character, self.currentSquare, self.currentMoveProps, ISMoveableCursor.mode[self.player], self.origSpriteName, self ));
            end
        end
    end
    self.cursorFacing = nil;
    self.joypadFacing = nil;
    self.objectListCache = nil;
end

function ISMoveableCursor:getInfoPanel()
    if not ISMoveableInfoWindow.infoPanels[self.player] then
        ISMoveableInfoWindow.infoPanels[self.player] = ISMoveableInfoWindow:new(0, 0, self.character );
    end

    return ISMoveableInfoWindow.infoPanels[self.player];
end

local int = 0;
function ISMoveableCursor:setInfoPanel( _square, _object, _moveProps, _customTexture)
    local infoPanel = self:getInfoPanel();
    if self.cursorFacing then -- when holding leftclick and dragging mouse to select a facing disable panel, otherwise panel will glitch visually on mouse over.
        infoPanel:setVisible(false);
        infoPanel:removeFromUIManager();
        self:setJoypadFocus( nil );
        return;
    end
    if _square and _moveProps then
        if not self.cacheObject or self.cacheObject ~= _object or not self.cacheSquare or self.cacheSquare ~= _square then
            infoPanel:setBodyText( _moveProps:getInfoPanelDescription( _square, _object, self.character, ISMoveableCursor.mode[self.player] ), UIFont.Small );
        end
        local mode = ISMoveableCursor.mode[self.player]
        local square = (mode == "pickup" or mode == "scrap") and _square
        infoPanel:setTexture( _customTexture or _moveProps.spriteName, self.canCreate, square, self.yOffset );
        infoPanel:setSquare(_square);
        infoPanel:setDrag(getCell():getDrag(self.player));
        infoPanel:setVisible(true);
        infoPanel:addToUIManager();
        self.cacheSquare = _square;
        self.cacheObject = _object;

    else --currently just set to invisible
        --if self.cacheObject then
            --if _square and not _square:getCanSee() then
                --infoPanel:setBodyText( ISMoveableSpriteProps.addLineToInfoTable( {}, "Cannot see target location.", 255, 0, 0 ), UIFont.Medium, "center" );
            --else
                --infoPanel:setBodyText( ISMoveableSpriteProps.addLineToInfoTable( {}, "No valid options.", 255, 0, 0 ), UIFont.Medium, "center" );
            --end
        --end
        self.cacheObject = nil;
        infoPanel:setTexture( nil, false, nil );
        infoPanel:setSquare(_square);
        infoPanel:setDrag(getCell():getDrag(self.player));
        infoPanel:setVisible(false);
        infoPanel:addToUIManager();
    end
    return infoPanel;
end

function ISMoveableCursor:render( _x, _y, _z, _square )
    self.renderX, self.renderY, self.renderZ = _x, _y, _z;

    local color = self.colorMod or {r=1,g=0,b=0};

    if self.currentMoveProps and self.currentMoveProps.scrapThumpable then
        local stairObjects = buildUtil.getStairObjects(self.currentMoveProps.object)
        if #stairObjects > 0 then
            for i=1,#stairObjects do
                stairObjects[i]:getSprite():RenderGhostTileColor(stairObjects[i]:getX(), stairObjects[i]:getY(), stairObjects[i]:getZ(), color.r, color.g, color.b, 0.8)
            end
        end
    end

    if self.currentMoveProps and self.currentMoveProps.isMultiSprite and self.currentMoveProps.sprite:getSpriteGrid()~=nil then
        if self.origMoveProps then
            local origGrid = self.origMoveProps.sprite:getSpriteGrid();
            local xo = origGrid:getSpriteGridPosX(self.origMoveProps.sprite);
            local yo = origGrid:getSpriteGridPosY(self.origMoveProps.sprite);

            local spriteGrid = self.currentMoveProps.sprite:getSpriteGrid();
            local w = spriteGrid:getWidth();
            local h = spriteGrid:getHeight();
            local wx, wy = _x-xo,_y-yo;
            for x=0, w-1 do
                for y=0,h-1 do
                    local square = getCell():getGridSquare(wx+x, wy+y, _z);
                    if square and square:getFloor() and square:getFloor():getSprite() then
                        square:getFloor():getSprite():RenderGhostTileColor(wx+x, wy+y, _z, 0.75, 1, 0.75, 0.25);
                    end

                    local objSprite = spriteGrid:getSprite(x, y);
                    if objSprite then
                        local yoffset =self.yOffset;
                        if ISMoveableCursor.mode[self.player] == "place" and self.currentMoveProps.name=="Police Sign" then --dirty hack :/
                            yoffset = self.currentMoveProps.surface;
                        end
                        objSprite:RenderGhostTileColor(wx+x, wy+y, _z, 0, yoffset * Core.getTileScale(), color.r, color.g, color.b, 0.8);
                    end
                end
            end
        end
    else
        if self.floorSprite then
            --self.floorSprite:RenderGhostTileColor(_x, _y, _z, color.r, color.g, color.b, 0.5);
            self.floorSprite:RenderGhostTileColor(_x, _y, _z, 0.75, 1, 0.75, 0.25);
        end
        if self.objectSprite then
            if self.currentMoveProps and self.currentMoveProps:canRotateDirection() then
                local dir = IsoDirections.Max
                local cursorFacing = self.cursorFacing or self.joypadFacing
                if cursorFacing then
                    local facing = { "N", "W", "S", "E" }
                    dir = IsoDirections[facing[cursorFacing]]
                end
                if instanceof(self.cacheObject, "Moveable") then
                    if IsoMannequin.isMannequinSprite(self.objectSprite) then
                        IsoMannequin.renderMoveableItem(self.cacheObject, _x, _y, _z, dir)
                        return
                    end
                end
                if instanceof(self.cacheObject, "IsoMannequin") then
                    IsoMannequin.renderMoveableObject(self.cacheObject, _x, _y, _z, dir)
                    return
                end
            end
            self.objectSprite:RenderGhostTileColor(_x, _y, _z, 0, self.yOffset * Core.getTileScale(), color.r, color.g, color.b, 0.8);

            --self.objectSprite:RenderGhostTileColor(_x, _y, _z, 0, self.yOffset, 1, 1, 1, 0.8);
        end
    end
end

function ISMoveableCursor:isValid( _square )
    if not self.floorSprite then
        self.floorSprite = IsoSprite.new();
        self.floorSprite:LoadFramesNoDirPageSimple('media/ui/FloorTileCursor.png');
    end

    self.currentMoveProps   = nil;
    self.origMoveProps      = nil;
    self.canCreate          = nil;
    self.objectSprite       = nil;
    self.origSpriteName     = nil;
    self.colorMod           = {r=1,g=0,b=0};
    self.yOffset            = 0;

    if ISMoveableCursor.mode[self.player] == "pickup" or ISMoveableCursor.mode[self.player] == "rotate" then
        self.objectIndex    = self.currentSquare ~= _square and -1 or self.objectIndex;
    end
    if _square ~= self.currentSquare then
        self.objectListCache = nil;
    end
    self.currentSquare  = _square;

    --if self.currentSquare == nil or not self.currentSquare:isCouldSee(self.player) then
    if self.currentSquare == nil then
        self:setInfoPanel( _square, nil, nil );
        self.cursorFacing = nil;
        self.joypadFacing = nil;
        return false;
    end

    if getPlayerRadialMenu(self.player) and getPlayerRadialMenu(self.player):isReallyVisible() then
        self:setInfoPanel( _square, nil, nil )
        self.cursorFacing = nil
        self.joypadFacing = nil
        return false
    end

    if self.character:getCharacterActions():isEmpty() then
        self.character:faceLocation(_square:getX(), _square:getY())
    end

    self.canSeeCurrentSquare = _square and _square:isCouldSee(self.player);

    if ISMoveableCursor.mode[self.player] == "pickup" then
        local objects = self.objectListCache or self:getObjectList();
        self.objectListCache = objects;

        if #objects > 0 then
            if self.objectIndex > #objects or self.objectIndex < 1 then self.objectIndex = 1 end
            if self.objectIndex >= 1 and self.objectIndex <= #objects then
                local object = not objects[self.objectIndex].isWall and objects[self.objectIndex].object or nil;
                local moveProps = objects[self.objectIndex].moveProps;

                if moveProps and moveProps.sprite then
                    --self:setInfoPanel( _square, object, moveProps );
                    self.currentMoveProps   = moveProps;
                    self.origMoveProps      = moveProps;
                    self.canCreate          = moveProps:canPickUpMoveable( self.character, _square, object );
                    self.colorMod           = ISMoveableCursor.normalColor; --self.canCreate and ISMoveableCursor.normalColor or ISMoveableCursor.invalidColor;
                    self.objectSprite       = nil; --moveProps.sprite; disabled object sprite for pickup
                    self.origSpriteName     = moveProps.spriteName;
                    --self.cursorFacing = nil;
                    self.yOffset            = moveProps:getYOffsetCursor(); -- this is updated in moveprops in canPickUpMoveable function
                    self:setInfoPanel( _square, object, moveProps );
                    return true;
                end
            end
        end
    elseif ISMoveableCursor.mode[self.player] == "place" then
        local objects = self.objectListCache or self:getInventoryObjectList();
        self.objectListCache = objects;

        if #objects > 0 then
            if self.objectIndex > #objects or self.objectIndex < 1 then self.objectIndex = 1 end
            if self.objectIndex >= 1 and self.objectIndex <= #objects then
                local item = objects[self.objectIndex].object;
                local moveProps = objects[self.objectIndex].moveProps;
                self.origMoveProps = moveProps;
                local origName = moveProps.spriteName;

                if moveProps and moveProps:hasFaces() then
                    local faceIndex;
                    if moveProps.isTableTop and not moveProps.ignoreSurfaceSnap then    -- adjustment for tabletops, they should always try to snap first if parent table has faces.
                        faceIndex = moveProps:snapFaceToSquare( _square ) or self.cursorFacing;
                    else
                        faceIndex = self.cursorFacing or moveProps:snapFaceToSquare( _square );
                    end
                    if faceIndex and moveProps:getIndexedFaces()[faceIndex] then
                        local tryMoveProps = ISMoveableSpriteProps.new( moveProps:getIndexedFaces()[faceIndex] );
                        if tryMoveProps and tryMoveProps.isMoveable and tryMoveProps.sprite then
                            --self.faceIndex = faceIndex;
                            moveProps = tryMoveProps;
                        end
                    end
                end

                if moveProps and moveProps.sprite then
                    --self:setInfoPanel( _square, item, moveProps );
                    self.currentMoveProps       = moveProps;
                    self.canCreate              = moveProps:canPlaceMoveable( self.character, _square, item );
                    self.colorMod               = self.canCreate and ISMoveableCursor.normalColor or ISMoveableCursor.invalidColor;
                    self.cacheInvObjectSprite   = item:getWorldSprite();
                    self.objectSprite           = moveProps.sprite;
                    self.origSpriteName         = origName;
                    --self.cursorFacing = nil;
                    self.yOffset                = moveProps:getYOffsetCursor(); -- this is updated in moveprops in canPlaceMoveable function
                    self:setInfoPanel( _square, item, moveProps );
                    return true;
                end

            end
        end
    elseif ISMoveableCursor.mode[self.player] == "rotate" then
        local rotateObject = self.objectListCache or self:getRotateableObject();
        self.objectListCache = rotateObject;
        if rotateObject then
            local object = rotateObject.object;
            local moveProps = rotateObject.moveProps;
            self.origMoveProps = moveProps;
            local origProps = moveProps;
            local origName = moveProps.spriteName;
            if moveProps and moveProps:hasFaces() then
                local faces = moveProps:getIndexedFaces();

                if self.objectIndex < 1 then
                    self.objectIndex = moveProps:getFaceIndex();
                end

                if self.objectIndex > #faces or self.objectIndex < 1 then self.objectIndex = 1 end
                local faceIndex = self.cursorFacing or self.objectIndex;

                if faceIndex >= 1 and faceIndex <= #faces and faces[faceIndex] then
                    local tryMoveProps = ISMoveableSpriteProps.new( faces[faceIndex] );
                    if tryMoveProps and tryMoveProps.isMoveable and tryMoveProps.sprite then
                        --self.faceIndex = faceIndex;
                        moveProps = tryMoveProps;
                    end
                end

                if moveProps and moveProps.sprite then
                    --self:setInfoPanel( _square, object, moveProps, faces[faceIndex] );
                    self.currentMoveProps   = moveProps;
                    self.canCreate          = moveProps:canRotateMoveable( _square, object, origProps ); --FIXME
                    self.colorMod           = self.canCreate and ISMoveableCursor.normalColor or ISMoveableCursor.invalidColor; --ISMoveableCursor.normalColor;
                    self.objectSprite       = moveProps.sprite;
                    self.origSpriteName     = origName;
                    self.yOffset            = moveProps:getYOffsetCursor();
                    self:setInfoPanel( _square, object, moveProps, faces[faceIndex] );
                    --self.cursorFacing = nil;
                    return true;
                end
            end
            if moveProps and moveProps.sprite and moveProps:canRotateDirection() then
                self.currentMoveProps   = moveProps;
                self.canCreate          = moveProps:canRotateMoveable( _square, object, origProps );
                self.colorMod           = self.canCreate and ISMoveableCursor.normalColor or ISMoveableCursor.invalidColor;
                self.objectSprite       = moveProps.sprite;
                self.origSpriteName     = origName;
                self.yOffset            = moveProps:getYOffsetCursor();
                self:setInfoPanel( _square, object, moveProps );
                return true;
            end
        end
    elseif ISMoveableCursor.mode[self.player] == "scrap" then
        local objects = self.objectListCache or self:getScrapObjectList();
        self.objectListCache = objects;
        if #objects > 0 then
            if self.objectIndex > #objects or self.objectIndex < 1 then self.objectIndex = 1 end
            if self.objectIndex >= 1 and self.objectIndex <= #objects then
                local object = objects[self.objectIndex].object;
                local moveProps = objects[self.objectIndex].moveProps;
                if moveProps and moveProps.sprite then
                    self.currentMoveProps   = moveProps;
                    self.origMoveProps      = moveProps;
                    self.canCreate          = moveProps:canScrapObject( self.character ).canScrap;
                    self.colorMod           = ISMoveableCursor.normalColor;
                    self.objectSprite       = nil;
                    self.origSpriteName     = moveProps.spriteName;
                    self.yOffset            = moveProps:getYOffsetCursor();
                    self:setInfoPanel( _square, object, moveProps );
                    return true;
                end
            end
        end
    end

    self:setInfoPanel( _square, nil, nil );
    self.cursorFacing = nil;
    self.joypadFacing = nil;
    return false;
end

function ISMoveableCursor:rotateKey(key, _joypadTriggered)
    if _joypadTriggered and (ISMoveableCursor.mode[self.player] == "place") then
        self.cursorFacing = nil
        self.joypadFacing = nil
    end
    if (key == getCore():getKey("Rotate building") or _joypadTriggered) and not self.cursorFacing then --disable key when rotating with mouse
        if ISMoveableCursor.mode[self.player] == "rotate" and not self.canCreate then
            --return;
        end
        self.cacheInvObjectSprite = nil;
        self.objectIndex = self.objectIndex + 1;

        if ISMoveableCursor.mode[self.player] == "rotate" and _joypadTriggered then
            local rotateObject = self.objectListCache or self:getRotateableObject()
            if rotateObject and rotateObject.moveProps and rotateObject.moveProps:canRotateDirection() then
                if not self.joypadFacing then
                    local dir = rotateObject.object:getDir()
                    local facing = { [IsoDirections.N]=1, [IsoDirections.W]=2, [IsoDirections.S]=3, [IsoDirections.E]=4 }
                    self.joypadFacing = facing[dir]
                end
                self.joypadFacing = self.joypadFacing + 1
                if self.joypadFacing > 4 then
                    self.joypadFacing = 1
                end
            end
        end
    end
end

function ISMoveableCursor:rotateWhilePlacing()
    local objects = self.objectListCache or self:getInventoryObjectList()
    local rotateObject = objects[self.objectIndex]
    if not rotateObject then return end
    if rotateObject.moveProps and rotateObject.moveProps:canRotateDirection() then
        if not self.joypadFacing then
            local dir = instanceof(rotateObject.object, "Moveable") and
                self:getDirectionFromItem(rotateObject.object) or rotateObject.object:getDir()
            local facing = { [IsoDirections.N]=1, [IsoDirections.W]=2, [IsoDirections.S]=3, [IsoDirections.E]=4 }
            self.joypadFacing = facing[dir]
        end
        self.joypadFacing = self.joypadFacing + 1
        if self.joypadFacing > 4 then
            self.joypadFacing = 1
        end
    elseif rotateObject.moveProps and rotateObject.moveProps:hasFaces() then
        if not self.cursorFacing then
            self.cursorFacing = rotateObject.moveProps:getFaceIndex()
        end
        self.cursorFacing = (self.cursorFacing or 0) + 1
        if self.cursorFacing > 4 then
            self.cursorFacing = 1
        end
    end
end

function ISMoveableCursor:getDirectionFromItem(item)
    if instanceof(item, "Moveable") then
        if IsoMannequin.isMannequinSprite(self.objectSprite) then
            return IsoMannequin.getDirectionFromItem(item, self.player)
        end
    end
    return IsoDirections.S
end

function ISMoveableCursor:rotateMouse(x, y)
    if self.currentSquare then
        if ISMoveableCursor.mode[self.player] == "rotate" and not self.canCreate then
            --return;
        end
        -- we start to get the direction the mouse is compared to the selected square for the item
        local difx = x - self.currentSquare:getX();
        local dify = y - self.currentSquare:getY();
        -- west
        if difx < 0 and math.abs(difx) > math.abs(dify) then
            self.cursorFacing = 2;-- "W";
        end
        -- east
        if difx > 0 and math.abs(difx) > math.abs(dify) then
            self.cursorFacing = 4; --"E";
        end
        -- north
        if dify < 0 and math.abs(difx) < math.abs(dify) then
            self.cursorFacing = 1; --"N";
        end
        -- south
        if dify > 0 and math.abs(difx) < math.abs(dify) then
            self.cursorFacing = 3; --"S";
        end
    end
end

function ISMoveableCursor:setJoypadFocus( _window )
    local joypadData = JoypadState.players[self.player+1]
    if joypadData then
        joypadData.focus = _window;
    end
end

function ISMoveableCursor:getRotateableObject()
    local square = self.currentSquare;
    if not square then return false end
    for i = square:getObjects():size(),1,-1 do
        local obj = square:getObjects():get(i-1);
        local moveProps = ISMoveableSpriteProps.new(obj:getSprite());
        if moveProps and moveProps:canManuallyRotate() then
            return { object = obj, moveProps = moveProps };
        end
    end
    return false;
end

function ISMoveableCursor:getInventoryObjectList()
    local objects           = {};
    local spriteBuffer	= {};
    local items 			= self.character:getInventory():getItems();
    local items_size 		= items:size();
    for i=0,items_size-1, 1 do
        local item = items:get(i);
        if instanceof(item, "Moveable") then
            if self.character:getPrimaryHandItem() ~= item and self.character:getSecondaryHandItem() ~= item then
                local moveProps = ISMoveableSpriteProps.new( item:getWorldSprite() );
                if moveProps.isMoveable then
                    local ignoreMulti = false
                    if moveProps.isMultiSprite then
                        local anchorSprite = moveProps.sprite:getSpriteGrid():getAnchorSprite()
                        if spriteBuffer[anchorSprite] then
                            ignoreMulti = true
                        else
                            spriteBuffer[anchorSprite] = true
                            if moveProps.sprite ~= anchorSprite then
                                moveProps = ISMoveableSpriteProps.new(anchorSprite)
                            end
                        end
                    end
                    if not ignoreMulti then
                        table.insert(objects, { object = item, moveProps = moveProps });
                        if self.cacheInvObjectSprite and self.cacheInvObjectSprite == item:getWorldSprite() then
                            self.objectIndex = #objects;
                        end
                    end
                end
            end
        end
    end

    if self.tryInitialInvItem then
        if instanceof(self.tryInitialInvItem, "Moveable") then
            --print("MovablesCursor attempting to set Initial Item: "..self.tryInitialInvItem:getWorldSprite());
            local moveProps = ISMoveableSpriteProps.new(self.tryInitialInvItem:getWorldSprite());
            local sprite = moveProps.sprite;
            if moveProps.isMultiSprite then
                local spriteGrid = moveProps.sprite:getSpriteGrid();
                sprite = spriteGrid:getAnchorSprite();
            end
            local spriteName = sprite:getName();
            for index,table in ipairs(objects) do
                --print("Compare "..table.object:getWorldSprite().." "..spriteName )
                if table.moveProps.sprite == sprite then
                    self.objectIndex = index;
                    self.cacheInvObjectSprite = spriteName;
                    break;
                end
            end
        else
            print("MovablesCursor Initial Item is not a Movable item");
            print(self.tryInitialInvItem);
        end
        self.tryInitialInvItem = nil;
    end

    return objects;
end

function ISMoveableCursor:shouldAddObject(_obj)
    --if we cannot see the square, only show for doors and windows
    if (not self.canSeeCurrentSquare) then
        --check if directly adjacent to square (avoid xray effect/picking up/breaking objects through blocked doors)
        if luautils.isSquareAdjacentToSquare(self.currentSquare, self.character:getSquare()) then
            if instanceof(_obj, "IsoDoor") or instanceof(_obj, "IsoWindow") then
                return true;
            end;
        end;
    else
        return true;
    end;
    return false
end

function ISMoveableCursor:getObjectList()
    local square = self.currentSquare;
    local objects = {};
    if not square then return objects end
    for i = square:getObjects():size(),1,-1 do
        local obj = square:getObjects():get(i-1);
        local moveProps = ISMoveableSpriteProps.new(obj:getSprite());
        if moveProps and moveProps.isMoveable then
            local add  = true;

            --[[if instanceof(obj,"IsoBarbecue") and obj:isLit() then
                add = false;
            end--]]

            if add then
                if self:shouldAddObject(obj) then
                    table.insert(objects, { object = obj, moveProps = moveProps, isWall = false });
                end;
            end;
        elseif moveProps and moveProps.spriteProps then
            if moveProps.spriteProps:Is("WallNW") or moveProps.spriteProps:Is("WallN") or moveProps.spriteProps:Is("WallW") then
                local sprList = obj:getChildSprites();
                if sprList then
                    local list_size 	= sprList:size();
                    if list_size > 0 then
                        for i=list_size-1, 0, -1 do
                            local sprite = sprList:get(i):getParentSprite();
                            local moveProps2 = ISMoveableSpriteProps.new( sprite );
                            if moveProps2.isMoveable and moveProps2.type == "WallOverlay" then
                                if self:shouldAddObject(obj) then
                                    table.insert(objects, { object = obj, moveProps = moveProps2, isWall = true });
                                end;
                            end;
                        end;
                    end;
                end;
            end;
        end;
    end;
    return objects;
end

function ISMoveableCursor:getScrapObjectList()
    local square = self.currentSquare;
    local objects = {};
    if not square then return objects end
    for i = square:getObjects():size(),1,-1 do
        local obj = square:getObjects():get(i-1);
        local moveProps = ISMoveableSpriteProps.fromObject(obj);
        if moveProps and moveProps.canScrap then
            if moveProps.material and ISMoveableDefinitions:getInstance().isScrapDefinitionValid( moveProps.material ) then
                if self:shouldAddObject(obj) then
                    table.insert(objects, { object = obj, moveProps = moveProps });
                end;
            elseif moveProps.scrapThumpable then
                if self:shouldAddObject(obj) then
                    table.insert(objects, { object = obj, moveProps = moveProps });
                end;
            end;
        end;
    end;
    return objects;
end

function ISMoveableCursor:onJoypadPressButton(joypadIndex, joypadData, button)
    local playerObj = getSpecificPlayer(joypadData.player)
    if button == Joypad.AButton then
        if self.canBeBuild and self.canCreate then
            self:tryBuild(self.xJoypad, self.yJoypad, self.zJoypad)
        else
            -- Hack to allow opening doors with A button
            local bp = getButtonPrompts(joypadData.player)
            bp:onAPress()
        end
    end

    if button == Joypad.BButton then
        getCell():setDrag(nil, joypadData.player);
    end

    if button == Joypad.XButton then
        if ISMoveableCursor.mode[self.player] == "place" and self.canCreate then
            local object = self.objectListCache[self.objectIndex].object
            local moveProps = self.objectListCache[self.objectIndex].moveProps
            if moveProps and (moveProps:hasFaces() or moveProps:canRotateDirection()) then
                self:rotateWhilePlacing()
            end
        end
    end
    
    if button == Joypad.YButton then
        local square = self.character:getCurrentSquare()
        if square then
            self.xJoypad = square:getX()
            self.yJoypad = square:getY()
        end
    end

    if button == Joypad.RBumper then
        self:rotateKey(0, true);
    end

    if button == Joypad.LBumper then
        ISMoveableCursor.changeModeKey( 0, joypadData.player, true );
    end
end

function ISMoveableCursor:getAPrompt()
    if self.canCreate then
        if ISMoveableCursor.mode[self.player] == "pickup" then
            if self.objectListCache and #self.objectListCache >= 1 then
                return getText("IGUI_PickupItem");
            end
        elseif ISMoveableCursor.mode[self.player] == "place" then
            if self.objectListCache and #self.objectListCache >= 1 then
				return getText("IGUI_PlaceObject");
				
            end
        elseif ISMoveableCursor.mode[self.player] == "rotate" then
            if self.objectListCache then
                return getText("IGUI_ApplyRotation");
            end
        elseif ISMoveableCursor.mode[self.player] == "scrap" then
            if self.objectListCache and #self.objectListCache >= 1 then
                return getText("IGUI_Scrap");
            end
        end
    end
    return nil
end

function ISMoveableCursor:getXPrompt()
    if ISMoveableCursor.mode[self.player] == "place" then
        if self.canCreate and self.objectListCache and (self.objectIndex >= 1) and (self.objectIndex <= #self.objectListCache) then
            local item = self.objectListCache[self.objectIndex].object;
            local moveProps = self.objectListCache[self.objectIndex].moveProps;
            if moveProps and (moveProps:hasFaces() or moveProps:canRotateDirection()) then
                return getText("IGUI_CycleRotation");
            end
        end
    end
    return nil
end

function ISMoveableCursor:getLBPrompt()
    return getText("IGUI_CycleMode");
end

function ISMoveableCursor:getRBPrompt()
    if ISMoveableCursor.mode[self.player] == "pickup" then
        if self.objectListCache and #self.objectListCache > 1 then
            return getText("IGUI_CycleObject");
        end
    elseif ISMoveableCursor.mode[self.player] == "place" then
        if self.objectListCache and #self.objectListCache > 1 then
            return getText("IGUI_CycleItems");
        end
    elseif ISMoveableCursor.mode[self.player] == "rotate" then
        if self.objectListCache and self.canCreate then --and #self.objectListCache >0 then
            return getText("IGUI_CycleRotation");
        end
    elseif ISMoveableCursor.mode[self.player] == "scrap" then
        if self.objectListCache and #self.objectListCache > 1 then
            return getText("IGUI_CycleObject");
        end
    end
	return nil
end

function ISMoveableCursor:new(_character)
    local o = {};
    setmetatable(o, self);
    self.__index = self;
    o:init();
    o.isMoveableCursor = true;
    o.moveableMode = "pickup";
    o.character = _character;
    o.player = _character:getPlayerNum();
    o.skipBuildAction = true;
    o.isYButtonResetCursor = true;
    o.noNeedHammer = false;
    o.skipWalk = true;
    o.renderFloorHelper = true;
    o.objectIndex = 1;
    o.subObjectIndex = 1;
    o.cacheInvObjectSprite = "";
    o.cacheInvObjectIndex = 1;
    o.renderX = -1;
    o.renderY = -1;
    o.renderZ = -1;
    o.yOffset = 0;
    o:setMoveableMode(ISMoveableCursor.mode[o.player] or "pickup");
    ISMoveableCursor.cursors[o.player] = o;
    return o;
end
